#include "Graphics.h"
#include <Windows.h>

#ifdef _DEBUG
#ifndef HR

#define HR(x)						\
	{								\
	HRESULT hr = x;					\
	if (FAILED(hr))					\
			{						\
			DXTRACE_ERR(#x, hr);	\
			DebugBreak();			\
			}						\
	}
#endif
#else
#ifndef HR
#define HR(x) x;
#endif
#endif

Graphics::Graphics()
	: m_iHeight(0), m_iWidth(0)
	, m_bInitialized(false)
	, m_bInitFailed(true)
	, m_bWindowed(true)
{}

/**
* This function need only be called once during the program
* It calls sub-routines that initializes various aspects of Graphics
*
* @param hWnd is passed so that the device knows which window to draw to
* @return wether or not the function ran without error
*/
bool Graphics::Init(HWND hWnd)
{

	if(m_bInitialized)					// prevent this function from being called more than once
		return !m_bInitFailed;
	m_bInitialized = true;				// flag that this function has been called

	/* if at any point a function fails, return false */
	if(!InitDevice(hWnd))
		return false;
	if(!InitManagers())
		return false;

	m_bInitFailed = false;
	return true;
}

bool Graphics::InitDevice(HWND hWnd)
{
	/* Create a temporary IDirect3D9 object used to setup the device */
	IDirect3D9* pD3DObject = NULL;
	pD3DObject = Direct3DCreate9(D3D_SDK_VERSION);

	if(NULL == hWnd)
		return false;

	/* Setup a Rect with the window dimensions to make sure they have been set */
	RECT rClientRect;
	if(!::GetClientRect(hWnd, &rClientRect))
		throw "GetClientRect() failed";
	g_scrHeight = m_iHeight = (int)(rClientRect.bottom - rClientRect.top);
	g_scrWidth  = m_iWidth  = (int)(rClientRect.right -  rClientRect.left);

	D3DCAPS9 D3DCaps;

	HR(pD3DObject->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &D3DCaps));
	//D3DCaps.TextureCaps = D3DPTEXTURECAPS_NONPOW2CONDITIONAL;

	// determine what type of vertex processing to use
	DWORD dwBehaviorFlags = 0;

	if(D3DCaps.VertexProcessingCaps != 0)
		dwBehaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
	else
		dwBehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;	

	//set up present parameters
	memset(&m_D3Dpp, 0, sizeof(m_D3Dpp));
	m_D3Dpp.hDeviceWindow			= hWnd;
	m_D3Dpp.Windowed				= true;				// SET TRUE FOR WINDOWED MODE
	m_D3Dpp.EnableAutoDepthStencil	= true;
	m_D3Dpp.AutoDepthStencilFormat	= D3DFMT_D16;
	m_D3Dpp.BackBufferCount			= 1;
	m_D3Dpp.BackBufferFormat		= D3DFMT_X8R8G8B8;
	m_D3Dpp.BackBufferWidth			= g_scrWidth;
	m_D3Dpp.BackBufferHeight		= g_scrHeight;
	m_D3Dpp.SwapEffect				= D3DSWAPEFFECT_DISCARD;
	m_D3Dpp.PresentationInterval	= D3DPRESENT_INTERVAL_DEFAULT;
	m_D3Dpp.Flags					= D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
	m_D3Dpp.MultiSampleQuality		= 0;
	m_D3Dpp.MultiSampleType			= D3DMULTISAMPLE_NONE;
	// setup the d3ddevice using the d3dobject and present parameters
	HRESULT hr = pD3DObject->CreateDevice(D3DADAPTER_DEFAULT, 
		D3DDEVTYPE_HAL, 
		hWnd,
		dwBehaviorFlags, 
		&m_D3Dpp,
		&m_pD3DDevice);
	pD3DObject->Release();
	pD3DObject = NULL;

	assert(hr == D3D_OK && "The device is broken");
	if(hr != D3D_OK)
		return false;

	m_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
	//m_pD3DDevice->SetRenderState(D3DRS_ZWRITEENABLE,true );
	//m_pD3DDevice->SetRenderState(D3DRS_ZFUNC,D3DCMP_LESSEQUAL );

	//m_pD3DDevice->SetRenderState(D3DRS_ZENABLE,D3DZB_TRUE );

	return true;
}

bool Graphics::InitManagers()
{
	HRESULT hr = D3DXCreateFont(m_pD3DDevice, 15, 0, NULL, 0, false,
		DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY,
		DEFAULT_PITCH | FF_DONTCARE, TEXT("Consolas"), &m_pFontManager);

	hr = D3DXCreateSprite(m_pD3DDevice, &m_pSpriteManager);
	return true;
}

/**
* Adds a single 2D object into the GameAsset list in Graphics
* 
* @param textureLoc is the filename of the asset
* @param cKey is the color key, put NULL if there isn't one
* @return a Vector2D, of which the x is which list and y is the index within that list
*/
int Graphics::AddSpriteAsset(const char* cszFileName, color* pColorKey, int iBlitFrames)
{
	// search through the asset list and make sure the asset isn't already there

	// with the texture generated, create a game asset with it and it it to the list
	return 0;
}

/**
* Adds a single 3D object into the GameAsset list in Graphics
* 
* @param  meshLoc is the filename of the .x file
* @param textureLoc is the filename of the texture to be put on the mesh
* @return a Vector2D, of which the x is which list and y is the index within that list
*/


bool Graphics::RemoveSpriteAsset(int assetID)
{
	return true;
}

Vector2D Graphics::GetAssetDimensions(int assetID)
{
	Vector2D dimensions(0, 0);
	LPDIRECT3DTEXTURE9 texture = NULL;
	D3DSURFACE_DESC desc;

	/*if(assetLoc.x < 0 || assetLoc.x >= ASSET_TYPES)
		texture = NULL;
	else
		texture = m_vAssetLists[(int)assetLoc.x].getByIndex((int)assetLoc.y).pObjTex;*/

	if(texture)
	{
		texture->GetLevelDesc(0, &desc);

		dimensions.x = (float)desc.Width;
		dimensions.y = (float)desc.Height;
	}

	return dimensions;
}


void Graphics::Update()
{}

/**
* Should be called in the game loop once; draws one frame
*
*/
void Graphics::Render()
{
	if(!m_bInitialized || m_bInitFailed)
		return;

	//m_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET , D3DXCOLOR(0.2f, 0.2f, 0.2f, 0.0f), 1.0f, 0);
	m_pD3DDevice->Clear(0, NULL, D3DCLEAR_ZBUFFER | D3DCLEAR_TARGET, D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f), 1.0f, 0);
	m_pD3DDevice->BeginScene();

	/* Draw game objects */

	m_pD3DDevice->EndScene();
	m_pD3DDevice->Present(NULL, NULL, NULL, NULL);
}

/**
* Responsible for drawing a single object
* @param pObj holds the address of the object
*/
void Graphics::DrawObject()
{
}

//void Graphics::DrawGUI(gui_panel* guiObj, GameAsset asset)
//{
//	if(asset.pObjTex == NULL) 
//		return;
//
//	m_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
//	m_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
//
//	Quad pos = guiObj->accessText(0).pos;
//	float x = (float)pos.x;
//	float y = (float)pos.y;
//	char* val;
//
//	//m_worldMat = CreateWorldMatrix(guiObj->getPos(),guiObj->getScale(),guiObj->getRotation());
//	//m_pSpriteManager->SetTransform(&m_worldMat);    //Move the sprite into place
//
//	//D3DXVECTOR3 drawPosition(guiObj->getPos().x,guiObj->getPos().y, 1.0f );
//
//	/* The process of drawing a GUI object involves first drawing a texture, then drawing all the text within */
//
//	D3DXMATRIX world, scale, trans, rot;
//	D3DXMatrixIdentity		(&rot);
//	D3DXMatrixScaling		(&scale, guiObj->getScale().x, guiObj->getScale().y, guiObj->getScale().z);
//	D3DXMatrixTranslation	(&trans, guiObj->getPos().x  , guiObj->getPos().y  , 1.0f);
//	D3DXMatrixMultiply		(&world,&rot,&trans);
//	D3DXMatrixMultiply		(&world,&scale,&trans);
//
//	m_pSpriteManager->SetTransform(&world);
//	m_pSpriteManager->Begin(D3DXSPRITE_ALPHABLEND);
//
//	if(asset.Color.red == -1)	m_ObjectColor = 0xFFFFFFFF;
//	else						m_ObjectColor = D3DCOLOR_XRGB(asset.Color.red,asset.Color.green,asset.Color.blue);
//
//	m_pSpriteManager->Draw(asset.pObjTex,asset.pBlitData,0,0,m_ObjectColor);  //Draw the sprite onto screen
//
//	//m_pSpriteManager->Draw(asset.pObjTex,		// the texture to use
//	//			asset.pBlitData,				// pSrcRect  - use the blit rect, if one exists
//	//			NULL,							// pCenter   - NULL specifies the upper left hand corner
//	//			&drawPosition,					// pPosition - position of the object
//	//			m_ObjectColor);					// Color	 - don't modulate color, use default
//
//	m_pSpriteManager->End();
//
//	m_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
//
//	/* draw each set of texts that are a part of the GUI object */ 
//	for(int i = 0; i < guiObj->numElements(); ++i)
//	{
//		pos = guiObj->accessText(i).pos;
//		RECT rPos = { pos.x, pos.y, pos.getMaxX(), pos.getMaxY() };
//		val = ((gui_panel*)(guiObj))->accessText(i).val;
//
//		m_pFontManager->DrawTextA(NULL,		// pSprite   - we don't need this, set to NULL 
//			val,							// pString   - the string to be drawn
//			-1,								// Count	 - num of characters in the string, set to -1 to use entire thing
//			&rPos,							// pRect	 - the rectangle location of the string
//			DT_LEFT,						// Format	 - how to format the string
//			0xFFFFFFFF);					// Color	 - don't modulate color, use default
//	}
//
//	//m_pSpriteManager->End();
//
//}


void Graphics::LoadTextureFromFile(const char* cszFileName, color* cKey, IDirect3DTexture9* & pTex)
{
	// determine whether or not the image has a color key, then set it
	D3DCOLOR colorKey = NULL;
	if(cKey != NULL)
		colorKey = D3DCOLOR_ARGB(255, cKey->red, cKey->green, cKey->blue);

	// use this function instead, it allows for more control in how it is setup
	D3DXCreateTextureFromFileEx(m_pD3DDevice,		// LPDIRECT3DDEVICE9 pDevice		the device
		cszFileName,								// LPCSTR pSrcFile					location of the file
		0,0,										// UINT Width, Height				prevents rounding to even binary numbers on dimensions
		0,											// UINT MipLevels					default mip mapping
		0,											// DWORD Usage						use non-dynamic texturing
		D3DFMT_UNKNOWN,								// D3DFORMAT Format					pixel formatting - takes directly from file
		D3DPOOL_MANAGED,							// D3DPOOL Pool						manage the memory class for us
		D3DX_DEFAULT,								// DWORD Filter						filter normally
		D3DX_DEFAULT,								// DWORD MipFilter					use normal mip filtering
		colorKey,									// D3DCOLOR ColorKey				use the color key if there is one
		NULL,										// D3DXIMAGE_INFO* pScrInfo			the image info, containing the texture's dimensions
		NULL,										// PALETTEENTRY* pPallete			we don't need palette, so NULL is used
		&pTex);										// LPDIRECT3DTEXTURE9* ppTexture	the texture to be received
}

void Graphics::Release()
{
	if(!m_bInitialized || m_bInitFailed)
		return;

	if(m_pD3DDevice != NULL)
	{
		m_pD3DDevice->Release();
		m_pD3DDevice = NULL;
	}
	if(m_pFontManager != NULL)
	{
		m_pFontManager->Release();
		m_pFontManager = NULL;
	}
	if(m_pSpriteManager != NULL)
	{
		m_pSpriteManager->Release();
		m_pSpriteManager = NULL;
	}
}

Graphics::~Graphics()
{
	Release();
}